package com.vortex.cloud.zhsw.xinyang.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.vortex.cloud.lbs.enums.CoordtypeEnum;
import com.vortex.cloud.lbs.enums.ShapeTypeEnum;
import com.vortex.cloud.zhsw.xinyang.service.ZipImportService;
import com.vortex.cloud.zhsw.xinyang.util.ExcelImportValidate;
import com.vortex.cloud.zhsw.xinyang.util.ExcelUtils;
import com.vortex.cloud.zhsw.xinyang.util.ZipUtils;
import com.vortex.cloud.sdk.api.service.IFileSdkService;
import com.vortex.cloud.vfs.lite.base.dto.AbstractBaseDTO;
import com.vortex.cloud.vfs.lite.base.dto.excel.ExcelReadDTO;
import com.vortex.cloud.vfs.lite.base.support.Constants;
import com.vortex.cloud.zhsw.xinyang.annotation.ImportAttachField;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author huohao@vortexinfo.cn
 * @date 2022/3/15 9:49
 */
@Service
@Slf4j
public class ZipImportServiceImpl implements ZipImportService {
    private final IFileSdkService fileSdkService;
    private final String temple = "/vortex/tmp";
    private final static String GET_PREFIX = "get";

    public ZipImportServiceImpl(IFileSdkService fileSdkService) {
        this.fileSdkService = fileSdkService;
    }

    /**
     * 读取Zip数据
     *
     * @param file                 Excel文件
     * @param clazz                读取的类型DTO
     * @param startRowNum          开始行
     * @param validateAndTransform 验证和信息（主要验证必填、格式、字典名称是否存在对应值、验重复，对于字典值将名称反写到对应字段）
     * @param coordtypeEnum        经纬度类型 {@link CoordtypeEnum}
     * @return com.vortex.cloud.vfs.lite.base.dto.excel.ExcelReadDTO<DTO>
     * @author huohao@vortexinfo.cn
     */
    @Override
    public <DTO extends AbstractBaseDTO<DTO>> ExcelReadDTO<DTO> readZip(MultipartFile file, Class<DTO> clazz, Integer startRowNum, ExcelImportValidate<DTO> validateAndTransform, CoordtypeEnum coordtypeEnum, ShapeTypeEnum shapeTypeEnum) throws IntrospectionException {
        Assert.notNull(file, "文件不能为空");
        String folderPath = ZipUtils.unZip(file, null);
        File folder = new File(folderPath);
        Assert.isTrue(folder.isDirectory(), "文件解压失败");
        try {
            String[] excelFile = folder.list((dir, name) -> name.endsWith(Constants.EXTENSION_XLS) || name.endsWith(Constants.EXTENSION_XLSX));
            Assert.notNull(excelFile, "压缩包无excel文件");
            Assert.isTrue(excelFile.length > 0, "压缩包无excel文件");
            String excelFileName = excelFile[0];
            File excel = new File(folderPath + File.separator + excelFileName);
            ExcelReadDTO<DTO> dtoExcelReadResult = ExcelUtils.readExcel(excel, clazz, 1, validateAndTransform, coordtypeEnum, shapeTypeEnum);
            if (Objects.isNull(dtoExcelReadResult) || CollectionUtils.isNotEmpty(dtoExcelReadResult.getMessages())) {
                return dtoExcelReadResult;
            }
            List<DTO> dataList = dtoExcelReadResult.getDatas();
            // 图片字段名称
            List<String> attachFieldList = Lists.newArrayList();
            // 图片字段get方法
            Map<String, Method> getMethodMap = Maps.newHashMap();
            // 图片字段set方法
            Map<String, Method> setMethodMap = Maps.newHashMap();
            // 获取图片字段
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(ImportAttachField.class)) {
                    attachFieldList.add(field.getName());
                    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                    getMethodMap.put(field.getName(), pd.getReadMethod());
                    setMethodMap.put(field.getName(), pd.getWriteMethod());
                }
            }
            if (CollectionUtils.isEmpty(attachFieldList)) {
                return dtoExcelReadResult;
            }
            dataList.forEach(entity -> {
                // 附件上传
                attachFieldList.forEach(fieldName -> {
                    try {
                        JSONArray fieldValue = new JSONArray();
                        String attachName = String.valueOf(getMethodMap.get(fieldName).invoke(entity));
                        if (StringUtils.isEmpty(attachName)) {
                            return;
                        }
                        String[] attachList = folder.list((dir, name) -> name.equals(attachName));
                        if (Objects.isNull(attachList) || attachList.length <= 0) {
                            return;
                        }
                        String attachFileName = attachList[0];
                        File attach = new File(folderPath + File.separator + attachFileName);
                        uploadFile(fieldValue, attach);
                        setMethodMap.get(fieldName).invoke(entity, fieldValue.toJSONString());
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        log.error("压缩文件导入失败", e);
                    }
                });
            });
            dtoExcelReadResult.setDatas(dataList);
            return dtoExcelReadResult;
        } finally {
            // 删除解压的文件夹
            this.deleteFile(folder);
        }
    }

    private boolean deleteFile(File file) {
        if (file.isDirectory()) {
            File[] childFiles = file.listFiles();
            if (Objects.nonNull(childFiles)) {
                for (File listFile : childFiles) {
                    deleteFile(listFile);
                }
            }
        }
        return file.delete();
    }

    private void uploadFile(JSONArray fieldValue, File attach) {
        if (attach.isDirectory()) {
            File[] childFiles = attach.listFiles();
            if (Objects.isNull(childFiles) || childFiles.length == 0) {
                return;
            }
            for (File listFile : childFiles) {
                uploadFile(fieldValue, listFile);
            }
        } else {
            String fileId = fileSdkService.uploadFile(attach.getName(), file2byte(attach));
            JSONObject fileJson = new JSONObject();
            fileJson.put("id", fileId);
            fileJson.put("name", attach.getName());
            fieldValue.add(fileJson);
        }
    }

    /**
     * 将文件转换成byte数组
     *
     * @param tradeFile 文件
     * @return byte[]
     * @author huohao@vortexinfo.cn
     */
    public static byte[] file2byte(File tradeFile) {
        byte[] buffer = null;
        try {
            FileInputStream fis = new FileInputStream(tradeFile);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (IOException e) {
            log.error("文件转byte数组异常", e);
        }
        return buffer;
    }
}
